#include "iotgo_platformAPI.h"
#include "iotgo_debug.h"

#include "iotgo_communicate_with_distribute_server.h"
#include "iotgo_memory_hooks.h"
#include "iotgo_mySocketClient_interface.h"
#include "cJSON.h"

#define GET_SERVER_INFO_DONE 1
#define DSERVER_DOMAIN_LENGTH 30

#define debugPrint distriServerDebugPrint

static volatile int state = DSERVER_IS_NOT_STARTED;
static MySocketClient *p_socket = NULL;
static uint8_t  dserver_domain[DSERVER_DOMAIN_LENGTH] = {0};
static uint32_t dserver_port = 0;

static int getLongServerInfo(void)
{
    uint8_t model[STRING_DEVICE_MODEL_LEN] = "wrong m!";
    uint8_t deviceID[STRING_DEVICE_ID_LEN] = "wrong ID!";
    uint8_t apikey[STRING_API_KEY_LEN] = "wrong apkey!";
    uint8_t rom_version[STRING_ROM_VERSION_LEN] = "wrong FW";
    uint8_t lserver_ip[STRING_LSERVER_IP_LEN] = {0};
    uint32_t lserver_port = 0;
    uint8_t flag = 0;
    int ret = 0;
    uint8_t buf[500] = {0};
    
    char *cjson_send = NULL;
    cJSON *cjson_Dserver = NULL;
    cJSON *cjson_root = NULL;
    cJSON *cjson_port = NULL;
    cJSON * cjson_server_ip = NULL;
    cJSON_Hooks memoryHook;
    char *cjson_print = NULL;


    debugPrint("[%s] => getLongServerInfo\n",__FUNCTION__);
    
    memoryHook.malloc_fn = pMalloc;
    memoryHook.free_fn = pFree;
    cJSON_InitHooks(&memoryHook);
    /*prepare for the message*/
    ret = getDeviceInfo(deviceID,apikey);
    ret = getDeviceModel(model);
    ret = getFWVersion(rom_version);
    
    if( (cjson_Dserver = cJSON_CreateObject()) != NULL)
    {
        cJSON_AddItemToObject(cjson_Dserver ,"accept" ,cJSON_CreateString("ws;2"));
        cJSON_AddItemToObject(cjson_Dserver ,"version" ,cJSON_CreateNumber(2));
        cJSON_AddItemToObject(cjson_Dserver ,"ts" ,cJSON_CreateNumber(933));
        cJSON_AddItemToObject(cjson_Dserver ,"deviceid" ,cJSON_CreateString(deviceID));
        cJSON_AddItemToObject(cjson_Dserver ,"apikey" ,cJSON_CreateString(apikey));
        cJSON_AddItemToObject(cjson_Dserver ,"model" ,cJSON_CreateString(model));
        cJSON_AddItemToObject(cjson_Dserver ,"romVersion" ,cJSON_CreateString(rom_version));
        cjson_send = cJSON_PrintUnformatted(cjson_Dserver);
    }
    else
    {
        debugPrint("[%s] cJSON_CreateObject() fails\n",__FUNCTION__);
        return -1;
    }

    debugPrint("[%s] cjson_send len is %d\n",
	       __FUNCTION__,strlen(cjson_send));
    debugPrint("[%s] dserver_domain is %s\n",
	       __FUNCTION__,dserver_domain);
    ret = sprintf(buf, "POST /dispatch/device HTTP/1.1\r\n"
		  "Host: %s\r\n"
		  "Requestid: 2\r\n"
		  "Content-Type: application/json\r\n"
		  "Content-Length: %d\r\n\r\n%s",
		  dserver_domain,
		  strlen(cjson_send),
		  cjson_send);

    debugPrint("[%s]write buf is \n%s\n",__FUNCTION__,buf);
    ret = writeToServer(p_socket,buf,ret);
    debugPrint("[%s]distr write ret is %d\n",__FUNCTION__,ret);
    if(cjson_send)
    {
        pFree(cjson_send);
    }
    
    cJSON_Delete(cjson_Dserver);
    memset(buf,0,500);
    ret = readFromServer(p_socket,buf,500);
    debugPrint("[%s] readFromServer return is %d\n",__FUNCTION__,ret);
    debugPrint("[%s] read buf is \n%s\n",__FUNCTION__,buf);
    if(ret > 0)
    {
        cjson_root = cJSON_Parse(strstr(buf,"\r\n\r\n"));
        if(cjson_root != NULL)
        {
            cjson_print = cJSON_PrintUnformatted(cjson_root);
            debugPrint("[%s] cjson_root is \n%s\n",__FUNCTION__,cjson_print);
            pFree(cjson_print);
            cjson_server_ip = cJSON_GetObjectItem(cjson_root,"IP");
            if(cjson_server_ip != NULL)
            {
                debugPrint("[%s] LServer IP is %s\n",__FUNCTION__,cjson_server_ip->valuestring);
                flag++;
            }
            cjson_port = cJSON_GetObjectItem(cjson_root,"port");
            if(cjson_port != NULL)
            {
                debugPrint("[%s] LServer port is %d\n",__FUNCTION__,cjson_port->valueint);
                flag++;
            }
            if(flag == 2)
            {
                setLServerInfo(cjson_server_ip->valuestring,
                                cjson_port->valueint);
            }
            cJSON_Delete(cjson_root);
        }
    }
    debugPrint("[%s] <= getLongServerInfo\n",__FUNCTION__);
    if(flag == 2)
    {
        return GET_SERVER_INFO_DONE;
    }

    return -1;
}

int communicateWithDistrServer(void)
{
    int8_t ret = -1;
    
    switch(state)
    {
    case DSERVER_IS_NOT_STARTED:
        /*init*/
        /*1. get lserver info*/
        ret = getDServerInfo(dserver_domain,&dserver_port);
        /*2. get the socket */
        if(ret == 0)
        {
            debugPrint("[%s]: next to setup p_socket\n",__FUNCTION__);
            p_socket = setupSocketClient(dserver_domain,dserver_port);
        }

        if(p_socket)
        {
            state = DSERVER_HAS_INITED;
            ret = DSERVER_ERROR_NOT_FINISHED;
        }
        else
        {
            debugPrint("[%s]: p_socket setup fail\n",__FUNCTION__);
            cleanWithDistrServer();
            ret = DSERVER_ERROR_INIT;
        }
        break;
    case DSERVER_HAS_INITED:
        /*try to get the sokcet ready*/
        debugPrint("[%s] connect to server\n",__FUNCTION__);
        ret = connectToServer(p_socket);
        if(ret == MYSOCKETCLIENT_SUCCESS)
        {
            state = DSERVER_SOCKET_IS_READY;
            ret = DSERVER_ERROR_NOT_FINISHED;
        }
        else if(ret == MYSOCKETCLIENT_FAIL)
        {
            ret = DSERVER_ERROR_CONNECT;
        }
        else
        {
            ret = DSERVER_ERROR_NOT_FINISHED;
        }
        break;
    case DSERVER_SOCKET_IS_READY:
        /*try to query the long server info*/
        debugPrint("[%s] communicate with dserver\n",__FUNCTION__);
        ret = getLongServerInfo();
        if(ret == GET_SERVER_INFO_DONE)
        {
            ret = DSERVER_ERROR_NONE;
            state = DSERVER_QUERY_IS_DONE;
        }
        else
        {
            ret = DSERVER_ERROR_QUERY;
        }
        break;
    default:
        ret = DSERVER_ERROR_INVALID_STATE;
        break;
    }
    return ret;
}

int cleanWithDistrServer(void)
{
    debugPrint("[%s]cleanWithDistrServer start\n",__FUNCTION__);
    print_memory_heap();
    cleanSocketClient(&p_socket);
    state = DSERVER_IS_NOT_STARTED;
    debugPrint("[%s]cleanWithDistrServer end\n",__FUNCTION__);
    print_memory_heap();
    return DSERVER_ERROR_NONE;
}

int getStateWithDistrServer(void)
{
    return state;
}
